package com.iot08.api.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;

import java.io.*;
import java.util.Properties;

/**
 * @author weichao 1299101858@qq.com
 * @version 1.0
 * @date 2021-07-22 17:57
 */
@Slf4j
public class ZLOpensslSign {
    private static String runCMD(String CMD) {
        Process process = null;
        String result = "";
        try {
            process = Runtime.getRuntime().exec(CMD);
            ByteArrayOutputStream resultOutStream = new ByteArrayOutputStream();
            InputStream errorInStream = new BufferedInputStream(process.getErrorStream());
            InputStream processInStream = new BufferedInputStream(process.getInputStream());
            int num = 0;
            byte[] bs = new byte[1024];
            while ((num = errorInStream.read(bs)) != -1) {
                resultOutStream.write(bs, 0, num);
            }
            while ((num = processInStream.read(bs)) != -1) {
                resultOutStream.write(bs, 0, num);
            }
            result = new String(resultOutStream.toByteArray(), "UTF-8");
//            System.out.println(result);
            log.info("运行的命令:{} 运行命令行的输出的log: {}", CMD, result);
            errorInStream.close();
            processInStream.close();
            resultOutStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (process != null) process.destroy();
        }
        return result;
    }

    public static String linuxGenEcdsaSign(String shellFilePath, String ProductId, String device_sn, String priv_key, String sign_file){
        //调取服务器上的 .sh 文件实现行获取sign
        String cmdStr = String.format("sh %s gen_ecdsa_sign %s %s %s %s",shellFilePath, ProductId, device_sn, priv_key, sign_file);
        runCMD(cmdStr);
        String sign = "";
        try {
            String line=null;
            File sign_fileFile = new File(sign_file);
            FileReader fr=new FileReader(sign_fileFile);
            BufferedReader br=new BufferedReader(fr);
            while((line=br.readLine())!=null) {
                //全部堆一起,不换行
//                sign += line+"\n";
                sign += line;
            }
            fr.close();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        return sign;
    }

    /**
     * 根据私钥生成签名
     * @param ProductId
     * @param device_sn
     * @param priv_key
     * @param sign_file
     * @return 签名
     */
    public static String GenEcdsaSign(String ProductId, String device_sn, String priv_key, String sign_file){
        String time_stamp = String.format("%d", System.currentTimeMillis());
        String ec_sign_info_file = "/tmp/ec_sign_info_file."+time_stamp;
        String ec_sign_info_sha256 = "/tmp/ec_sign_info_sha256."+time_stamp;
        String ec_binary_sign_file = "/tmp/ec_binary_sign_file."+time_stamp;
        File sign_fileFile = new File(sign_file);
        File ec_sign_info_fileFile = new File(ec_sign_info_file);
        try {
            ec_sign_info_fileFile.createNewFile();
            new File(ec_sign_info_sha256).createNewFile();
            new File(ec_binary_sign_file).createNewFile();
            //写数据
            Writer w=new FileWriter(ec_sign_info_fileFile);
            w.write(String.format("-n %s_%s\n",ProductId, device_sn));//write()里可以写任意字符串
            w.close();
        }catch (IOException error){
            // 创建失败
            error.printStackTrace();
        }

        //需要指定openssl的路径 (如果配置了环境变量也可不用)
        //Sha256 加密
        String cmdSha256 = String.format("openssl dgst -sha256 -binary -out %s %s",ec_sign_info_sha256, ec_sign_info_file);
        //pkeyutl 非对称加密
        String cmdPkeyutl = String.format("openssl pkeyutl -sign -in %s -out %s -inkey %s -keyform PEM",ec_sign_info_sha256, ec_binary_sign_file, priv_key);
        // base64 加密一下
        String cmdBase64 = String.format("openssl base64 -e -in %s -out %s", ec_binary_sign_file, sign_file);

        runCMD(cmdSha256);
        runCMD(cmdPkeyutl);
        runCMD(cmdBase64);
        String sign = "";
        try {
            String line=null;
            FileReader fr=new FileReader(sign_fileFile);
            BufferedReader br=new BufferedReader(fr);
            while((line=br.readLine())!=null) {
                sign += line+"\n";
            }
            fr.close();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }


        boolean res1 =removeFile(ec_sign_info_file);
        boolean res2 =removeFile(ec_sign_info_sha256);
        boolean res3 =removeFile(ec_binary_sign_file);
        return sign;
    }

    /**
     * 使用公钥验证签名
     * @param ProductId
     * @param device_sn
     * @param pub_key
     * @param sign_file
     * @return 是否验证通过
     */
    public static boolean VerifyEcdsaSign(String ProductId, String device_sn, String pub_key, String sign_file){
        String time_stamp = String.format("%d", System.currentTimeMillis());
        String ec_sign_info_file = "/tmp/ec_sign_info_file."+time_stamp;
        String ec_sign_info_sha256 = "/tmp/ec_sign_info_sha256."+time_stamp;
        String ec_binary_sign_file = "/tmp/ec_binary_sign_file."+time_stamp;

//        File sign_fileFile = new File(sign_file);
        File ec_sign_info_fileFile = new File(ec_sign_info_file);
        try {
            ec_sign_info_fileFile.createNewFile();
            new File(ec_sign_info_sha256).createNewFile();
            new File(ec_binary_sign_file).createNewFile();
            //写数据
            Writer w=new FileWriter(ec_sign_info_fileFile);
            w.write(String.format("-n %s_%s\n",ProductId, device_sn));//write()里可以写任意字符串
            w.close();
        }catch (IOException error){
            // 创建失败
            error.printStackTrace();
        }

        //需要指定openssl的路径 (如果配置了环境变量也可不用)
        //Sha256 加密
        String cmdSha256 = String.format("openssl dgst -sha256 -binary -out %s %s",ec_sign_info_sha256, ec_sign_info_file);

        // base64 解密一下
        String cmdBase64 = String.format("openssl base64 -d -in %s -out %s", sign_file, ec_binary_sign_file);

        //pkeyutl 验证
        String cmdPkeyutl = String.format("openssl pkeyutl -verify -in %s -sigfile %s -pubin -inkey %s -keyform PEM ",ec_sign_info_sha256, ec_binary_sign_file, pub_key);

        runCMD(cmdSha256);
        runCMD(cmdBase64);
        String res = runCMD(cmdPkeyutl);

        boolean res1 =removeFile(ec_sign_info_file);
        boolean res2 =removeFile(ec_sign_info_sha256);
        boolean res3 =removeFile(ec_binary_sign_file);
        if(res.contains("Successfully")){
            return true;
        }else{
            return false;
        }

    }

    private static boolean removeFile(String path){
        File file = new File(path);
        if(file.exists()) {
            file.delete();
            return true;
        }
        return false;
    }


    /**
     * 根据imei 生成 GUID (手表QQ登录所需参数标识)
     * @param imei
     * @param GUIDFilePath GUID 的存储路径
     * @return GUID
     */
    public static String getGUIDWithImei(String imei, String GUIDFilePath){
        // md5 加密 小写
        String GUID = DigestUtils.md5Hex(imei);
        File GUIDFile = new File(GUIDFilePath);
        try {
            if(!GUIDFile.exists()){
                //如果不存在则创建
                final boolean newFile = GUIDFile.createNewFile();
            }
            //写数据
            Writer w=new FileWriter(GUIDFile);
            w.write(GUID);//write()里可以写任意字符串
            w.close();
        }catch (IOException error){
            // 创建失败
            log.info("创建 GUIDFilePath:{} 失败 error :{}",GUIDFilePath, error.getMessage());
            log.error("创建 失败:error:"+error.getMessage(),error);
//            error.printStackTrace();
        }
        return GUID;
    }



    /**
     * 根据GUID 生成 License (手表QQ登录所需参数)
     * @param toolPath 工具路径
     * @param GUIDFilePath GUID 的存储路径
     * @param LicenseFilePath License 的存储路径
     * @return License
     */
    public static String getLicenseWithGUID(String toolPath, String GUIDFilePath, String LicenseFilePath){

        File LicenseFile = new File(LicenseFilePath);
        try {
            if(!LicenseFile.exists()){
                //如果不存在则创建
                final boolean newFileres = LicenseFile.createNewFile();
            }
        }catch (IOException error){
            // 创建失败
            log.info("创建 LicenseFilePath:{} 失败 error :{}",LicenseFilePath, error.getMessage());
            log.error("创建 失败:error:"+error.getMessage(),error);
        }
        //需要先移动到目录下
        String cdPathCMD = "cd "+toolPath.substring(0,toolPath.lastIndexOf("/"));
        runCMD(cdPathCMD);
        String cmd = String.format("%s Generate %s %s", toolPath, GUIDFilePath, LicenseFilePath);
        runCMD(cmd);

        String license = "";
        try {
            String line=null;
            FileReader fr=new FileReader(LicenseFilePath);
            BufferedReader br=new BufferedReader(fr);
            while((line=br.readLine())!=null) {
                license += line+"\n";
            }
            fr.close();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        String[] tempStrList = license.split(" ");
        license = tempStrList[tempStrList.length-1].replace("\n", "");
        return license;
    }

    /**
     * 校验 GUID 和 License 是否正确
     * @param toolPath 工具路径
     * @param GUIDFilePath GUID 的存储路径
     * @param LicenseFilePath License 的存储路径
     * @return 校验结果
     */
    public static String verifyGUIDAndLicense(String toolPath, String GUIDFilePath, String LicenseFilePath){
        // md5 加密 小写
        String cmd = String.format("%s Verify %s %s",toolPath, GUIDFilePath, LicenseFilePath);

        return runCMD(cmd);
    }



    public static void main(String[] args) throws Exception {

//        String ProductId = "2995";
//        String device_sn = "123456789000008";
//        String sign = ZLOpensslSign.GenEcdsaSign("2995", "123456789000008","priv_key", "sign_file");
//
//        System.out.println("ProductId:"+ProductId+" dec_id:"+device_sn+" 签完名:"+sign);
//
//        System.out.println(String.format("验证:============"));
//        ZLOpensslSign.VerifyEcdsaSign("2995", "123456789000008","pub_key", "sign_file");
//

        Properties props=System.getProperties(); //系统属性
        System.out.println("用户的当前工作目录："+props.getProperty("user.dir"));
        String TXHLQQ_GUIDAndLicenseldFolderPath = "/Users/apple/Documents/Watch项目使用资料/智能守护5相关资料/安卓设备QQ和微信对接文档/腾讯互联-设备QQ/tencentLicenseTool/output/";
        String TXHLQQ_ToolPath = "/Users/apple/Documents/Watch项目使用资料/智能守护5相关资料/安卓设备QQ和微信对接文档/腾讯互联-设备QQ/tencentLicenseTool/output/tencentLicenseTool-x86_64";
        String imei = "32156";
        String GUIDFilePath = TXHLQQ_GUIDAndLicenseldFolderPath+imei+"guid.txt";
        String LicenseldFilePath = TXHLQQ_GUIDAndLicenseldFolderPath+imei+"licenseld.txt";
        String GUID = ZLOpensslSign.getGUIDWithImei(imei, GUIDFilePath);
        String deviceLicenseld = ZLOpensslSign.getLicenseWithGUID(TXHLQQ_ToolPath, GUIDFilePath, LicenseldFilePath);
        System.out.println(String.format("GUID:%s deviceLicenseld:%s", GUID, deviceLicenseld));

    }

}
